//////////////////////////////////////////////////////////////////
// MattiseOS Kernel
// Date: 2008
// Author: Matthew Iselin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////

// Includes
#include <mattise.h>
#include <stdint.h>
#include <string.h>
#include <mem/util.h>
#include <mem/heap.h>
#include <kernio/io.h>
#include <devman/devman.h>
#include <process/common.h>

// Defines

// the type of I/O to perform (synchronous is not a good idea if working with
// shells, simply because the read() system call will block and no output
// can be made until the bytes have been read in)
//#define DEVREQ_ASYNCHRONOUS
#define DEVREQ_SYNCHRONOUS

// debugging mode?
#define DEBUG 0

// Types

// a device in the device tree
struct dev {
    struct dev* prev;       // the previous device in the list

    char prefix[256];       // the prefix (ie, for tty would be "/dev/tty")

    int id;                 // the device id

    uint32_t sys_open;		// the device driver call locations
    uint32_t sys_read;
    uint32_t sys_write;
    uint32_t sys_seek;
    uint32_t sys_ioctl;
    uint32_t sys_stat;
    uint32_t sys_close;

    int blksz;				// the size of a block (1 = char, > 1 = block)

    struct dev* next;		// the next device in the list
} *devlist = 0;

// Variables

// the io request list
struct ioreq* ioreqlist = 0;

// set to -1 when a function fails (used to allow any return value from functions
// while still permitting error handling)
uint32_t devman_errno;

// Functions

#define PERFORMIO_NOT_THREAD

// performs I/O on a specific request - MUST BE A THREAD
void performio( struct ioreq* req, uint32_t reqtype )
{
	//dprintf("=============== performio ===============\n");

	// please interrupt this code
	asm volatile( "sti" );

	// get the request itself
	//uint32_t req_location = *((uint32_t*) argv[0]);
	//struct ioreq* req = (struct ioreq*) req_location;

	// and then the type
	//uint32_t reqtype = *((uint32_t*) argv[1]);

	// free the arguments
	//free( argv );
	//argv = 0;

#ifndef PERFORMIO_NOT_THREAD

	// grab the process - all I/O is on a wait queue after it's requested
	struct pentry* ent;
	for( ent = p_waitq; ent != 0; ent = ent->next )
		if( ent && ent->me )
			if( ent->me->id == req->pid )
				break;

	// valid entry?
	if( ent == 0 || ent->me == 0 || ent->me->isprocess )
#ifdef PERFORMIO_NOT_THREAD
		return;
#else
		kill( getpid() ); // no!
#endif

#else
	struct pentry* ent = currprocess->ent;
#endif

	// we have a valid entry, so grab the file descriptor
	struct fd desc = ent->me->filedescs[req->fdnum];

	// grab the device
	struct dev* device;
	size_t i = 0;
	for( device = devlist; device != 0 && device->id != desc.devid; device = device->next, i++ );

	// check for a valid device
	if( device == 0 )
	{
		ent->me->status = PSTATE_READY;
		req->reqres = -1;
#ifdef PERFORMIO_NOT_THREAD
		return;
#else
		wake(ent->me);
		kill( getpid() ); // no!
#endif
	}

	// perform the correct action
	switch( reqtype )
	{
		// open call: returns an internal descriptor
		case DEVREQ_OPEN:
			{

				// look for a valid call
				if( device->sys_open )
				{
					// get the call
					typedef int32_t (*opencall)(const char*,int32_t);
					opencall r = (opencall) device->sys_open;
					req->reqres = (int) r( req->kbuff, req->iarg1 );
				}
				else
					req->reqres = -1;

			}
			break;

		// close call: closes a descriptor
		case DEVREQ_CLOSE:
			{

				// look for a valid call
				if( device->sys_close )
				{
					// call it
					typedef int32_t (*closecall)(int32_t);
					closecall r = (closecall) device->sys_close;
					req->reqres = (int) r( desc.internaldesc );
				}
				else
					req->reqres = -1;

			}
			break;

		// dup call: duplicates a file descriptor
		case DEVREQ_DUP:
			{
				dprintf("[DEVM ] performio(): dup not implemented in drivers yet\n");
				req->reqres = req->iarg1;
			}
			break;

		// read call: reads a number of blocks of data
		case DEVREQ_READ:
			{

				// look for a valid call
				if( device->sys_read )
				{
					// call it
					typedef size_t (*readcall)(int32_t,void*,size_t);
					readcall r = (readcall) device->sys_read;
					req->reqres = (int) r( desc.internaldesc, req->kbuff, req->bcount );
				}
				else
					req->reqres = -1;

			}
			break;

		// write call: writes a number of blocks of data
		case DEVREQ_WRITE:
			{

				// look for a valid call
				if( device->sys_write )
				{
					// call it
					typedef size_t (*writecall)(int32_t,void*,size_t);
					writecall r = (writecall) device->sys_write;
					req->reqres = (int) r( desc.internaldesc, req->kbuff, req->bcount );
				}
				else
					req->reqres = -1;

			}
			break;

		// seek call: seeks internally
		case DEVREQ_SEEK:
			{

				// look for a valid call
				if( device->sys_seek )
				{
					// call it
					typedef size_t (*seekcall)(int32_t,size_t,int32_t);
					seekcall r = (seekcall) device->sys_seek;
					req->reqres = (int) r( desc.internaldesc, req->iarg1, req->iarg2 );
				}
				else
					req->reqres = -1;

			}
			break;

		// ioctl call: provides control over the I/O device
		case DEVREQ_IOCTL:
			{

				// look for a valid call
				if( device->sys_ioctl )
				{
					// call it
					typedef int32_t (*ioctlcall)(int32_t,int32_t,int32_t);
					ioctlcall r = (ioctlcall) device->sys_ioctl;
					req->reqres = (int) r( desc.internaldesc, req->iarg1, req->iarg2 );
				}
				else
					req->reqres = -1;

			}
			break;

		// stat call: allows the caller to get information about the open file
		case DEVREQ_STAT:
			{

				// look for a valid call
				if( device->sys_stat )
				{
					// call it
					typedef int32_t (*statcall)(int32_t,struct stat*);
					statcall r = (statcall) device->sys_stat;
					req->reqres = (int) r( desc.internaldesc, (struct stat*) req->kbuff );
				}
				else
					req->reqres = -1;

			}
			break;
	}

	// kill this thread, we're done here
#ifdef PERFORMIO_NOT_THREAD

	return;

#else

	// IO is done, so the process is now ready to return
	ent->me->status = PSTATE_READY;
	wake(ent->me);
	kill( getpid() );

#endif
}

// handles I/O requests
void HandleIO()
{
#ifdef PERFORMIO_NOT_THREAD
	//
#else
	dprintf("HandleIO\n");

	// check the I/O list for any pending requests that we should handle
	struct ioreq* req;
	for( req = ioreqlist; req != 0; req = req->next )
	{
		// does this require I/O?
		if( req )
		{
			if( req->req != DEVREQ_INVALID )
			{
				// save the request
				uint32_t reqtype = req->req;

				// make the request invalid, so that the new thread can run without requiring a lock
				req->req = DEVREQ_INVALID;

				// make up a quick argument list
				char** alist = (char**) malloc( sizeof( char* ) * 3 );
				alist[0] =  (char*) malloc( sizeof( uint32_t ) ); // first argument: the request itself
				*((uint32_t*) alist[0]) = (uint32_t) req;
				alist[1] = (char*) malloc( sizeof( uint32_t ) ); // second argument: request type
				*((uint32_t*) alist[1]) = reqtype;
				alist[2] = 0; // null pointer at the end as always
				//performio( 2, alist );
				kCreateThread( currprocess->parentid, (uint32_t) performio, TF_NONE, 10, alist, 0 );
			}
		}
	}
#endif
}

// sets up the device manager
void SetupDevMan()
{
	// setup the request list
	ioreqlist = (struct ioreq*) malloc( sizeof( struct ioreq ) );
	memset( ioreqlist, 0, sizeof( struct ioreq ) );

	// setup the device list
	devlist = (struct dev*) malloc( sizeof( struct dev ) );

	// setup the LL pointers
	devlist->prev = 0;
	devlist->next = 0;

	// set the prefix
	memset( devlist->prefix, 0, 256 );

	// set its id
	devlist->id = -1;

	// setup the block size
	devlist->blksz = 0;

	// no errors yet
	devman_errno = 0;
}

// installs a new device
void InstallDevice(	const char* prefix,
					uint32_t s_open,
					uint32_t s_read,
					uint32_t s_write,
					uint32_t s_seek,
					uint32_t s_ioctl,
					uint32_t s_stat,
					uint32_t s_close,
					int blocksize
					)
{
	// we're toying with the linked lists, so we don't want interruptions
	uint32_t intstate;
	asm volatile( "pushf; pop %0; cli" : "=r" (intstate) );

	// allocate space for the new device
	struct dev* newdev = (struct dev*) malloc( sizeof( struct dev ) );

	// write to the debug log
#if DEBUG
	dprintf( "Installing device '%s': o=%x, r=%x, w=%x, s=%x, i=%x, st=%x, c=%x\n",
		prefix,
		s_open,
		s_read,
		s_write,
		s_seek,
		s_ioctl,
		s_stat,
		s_close
	);
#endif

	// setup the LL pointers
	newdev->next = 0;

	// setup its calls
	newdev->sys_open = s_open;
	newdev->sys_write = s_write;
	newdev->sys_read = s_read;
	newdev->sys_seek = s_seek;
	newdev->sys_ioctl = s_ioctl;
	newdev->sys_stat = s_stat;
	newdev->sys_close = s_close;

	// setup its block size
	newdev->blksz = blocksize;

	// find a place to insert it
	struct dev* tmp, *prev = 0;
	size_t i = 0;
	for( tmp = devlist; tmp != 0; prev = tmp, tmp = tmp->next, i++ );

	// check for a valid pointer
	if( prev == 0 )
	{
		// free up the new pointer
		free( newdev );

		// re-enable interrupts
		if( intstate & 0x200 )
			asm volatile( "sti" );

		// failed, so just return
		devman_errno = E_DMAN_NOROOM;
		return;
	}

	// we have a valid pointer, so set it up
	memcpy( newdev->prefix, prefix, strlen( prefix ) + 1 );
	newdev->id = i;
	newdev->prev = prev;
	newdev->next = prev->next;
	if( prev->next )
		prev->next->prev = newdev;
	prev->next = newdev;

	// re-enable interrupts
	if( intstate & 0x200 )
		asm volatile( "sti" );

	// no error
	devman_errno = E_DMAN_NOERR;
}

#define DEVMAN_MEM_FREE( p ) ( free( p ) )
#define USE_GIVEN_PTR 1 // whether to use the given buff pointer or to allocate space within the kernel
#define USE_STACK_NOT_MALLOC 1 // whether to allocate requests on the stack or the heap

// common I/O routine handling
int32_t commonio_handling( int32_t reqtype, int32_t fd, const char* buff, size_t count, int32_t iarg1, int32_t iarg2, int32_t iarg3, int32_t iarg4 )
{
	// we're toying with process data so don't let us be interrupted
	uint32_t intstate;
	asm volatile( "pushf; pop %0; cli" : "=r" (intstate) );

	// check fd
	if( fd < 0 || fd > 512 )
	{
		// re-enable interrupts
		if( intstate & 0x200 )
			asm volatile( "sti" );
		devman_errno = E_DMAN_INVALARG;
		return -1;
	}

	// grab the descriptor
	struct fd desc = currprocess->filedescs[fd];
	if( desc.avail )
	{
		// re-enable interrupts
		if( intstate & 0x200 )
			asm volatile( "sti" );
		devman_errno = E_DMAN_NOROOM;
		return -1;
	}

	// find the device pointer
	struct dev* device;
	size_t i = 0;
	for( device = devlist; device != 0; device = device->next )
	{
		if(device->id == desc.devid)
			break;
		i++;
	}

	// does it exist?
	if( device == 0 )
	{
		// re-enable interrupts
		dprintf( "null device - reqtype=%d and fd=%d!\n", reqtype, fd );
		if( intstate & 0x200 )
			asm volatile( "sti" );
		devman_errno = E_DMAN_INVALARG;
		return -1;
	}

	// place this onto the request queue for dealing with later
#if USE_STACK_NOT_MALLOC
	struct ioreq real_stack_req;
#endif
	struct ioreq* req = (struct ioreq*)
#if USE_STACK_NOT_MALLOC
	&real_stack_req;
#else
	malloc( sizeof( struct ioreq ) );
#endif
	if( !req ) { devman_errno = E_DMAN_OUTOFMEM; return -1; };
	memset( req, 0, sizeof( struct ioreq ) );
	req->devid = desc.devid;
	req->req = reqtype;
	req->pid = desc.pid;
	req->fdnum = fd;
	req->reqres = 0;
	if( reqtype == DEVREQ_OPEN )
		req->bytecount = req->bcount = strlen( buff ) + 1;
	else if( reqtype == DEVREQ_STAT )
		req->bytecount = req->bcount = sizeof( struct stat );
	else
	{
		req->bcount = count;
		req->bytecount = count * device->blksz;
	}
	req->kbuff = (char*)
#if USE_GIVEN_PTR
	buff;
#else
	malloc( req->bytecount );
#endif
	req->kbuff_phys = req->kbuff;
	req->iarg1 = iarg1;
	req->iarg2 = iarg2;
	req->iarg3 = iarg3;
	req->iarg4 = iarg4;

	// find a place to insert it
#if !USE_STACK_NOT_MALLOC
	struct ioreq* tmp = ioreqlist, *prev = 0;
	while( tmp != 0 )
	{
		prev = tmp;
		tmp = tmp->next;
	}

	// verify that we got a place
	if( prev == 0 )
	{
		// free the request
#if !USE_GIVEN_PTR
		DEVMAN_MEM_FREE( req->kbuff_phys );
#endif
		free( req );

		// re-enable interrupts
		if( intstate & 0x200 )
			asm volatile( "sti" );
		devman_errno = E_DMAN_NOROOM;
		return -1;
	}

	// insert it
	if( prev == req )
	{
		dprintf( "FAILURE - I/O REQUEST LIST CORRUPTION!\n" );
		kill( getpid() ); while( 1 );
	}
	else
	{
		req->prev = prev;
		req->next = prev->next;
		if( prev->next )
			prev->next->prev = req;
		prev->next = req;
	}
#endif

	// zero the buffer - is this just wasting cycles?
	//memset( req->kbuff, 0, req->bytecount );

	// does the request require the use of a kernel buffer?
#if !USE_GIVEN_PTR
	if(	buff )
	{
		// copy the passed buffer across
		if( reqtype != DEVREQ_READ )
			memcpy( req->kbuff, buff, req->bytecount );
	}
	else
	{
		// this used to be done, now i realise it probably isn't a good idea
		//DEVMAN_MEM_FREE( req->kbuff_phys );
		//req->kbuff_phys = 0;
	}
#endif

#ifdef PERFORMIO_NOT_THREAD
	// make the request invalid, so that the new thread can run without requiring a lock
	req->req = DEVREQ_INVALID;

	// make up a quick argument list
	/*char** alist = (char**) malloc( sizeof( char* ) * 3 );

	alist[0] =  (char*) malloc( sizeof( uint32_t ) ); // first argument: the request itself
	*((uint32_t*) alist[0]) = (uint32_t) req;
	alist[1] = (char*) malloc( sizeof( uint32_t ) ); // second argument: request type
	*((int32_t*) alist[1]) = reqtype;
	alist[2] = 0; // null pointer at the end as always*/
#if DEBUG
	dprintf("creating IO thread\n");
#endif
	//int32_t newthread = kCreateThread( currprocess->parentid, (uint32_t) performio, TF_NONE, 10, alist );

	performio(req, reqtype);

	asm volatile("cli");
#endif

	// the current task is now waiting for I/O
	//currprocess->status = PSTATE_WAITING;
	/*dprintf("sleeping\n");
	wait();
	dprintf("woken\n");*/

	// reschedule
	//kSchedule( 0 );

	// check that the request went through properly
	if( req->reqres == -1 )
	{
		// free the allocated pages
#if !USE_GIVEN_PTR
		DEVMAN_MEM_FREE( req->kbuff_phys );
#endif
		req->kbuff = 0;
		req->kbuff_phys = 0;

		// unlink the request
#if !USE_GIVEN_PTR
		if(req->prev)
			req->prev->next = req->next;
		if( req->next )
			req->next->prev = req->prev;
#endif

		// free the request
#if !USE_STACK_NOT_MALLOC
		free( req );
#endif

		// this is special case - nothing should reutrn -1 unless it *FAILED*
		// at doing whatever it was hoping to do, so we tell the programmer
		// we failed
		devman_errno = E_DMAN_FAILED;

		// re-enable interrupts, we're finished
		if( intstate & 0x200 )
			asm volatile( "sti" );

		// no, so just return
		return -1;
	}

	// copy the buffers if needed
#if !USE_GIVEN_PTR
	if( buff )
	{
		if( reqtype != DEVREQ_OPEN )
		{
			if( reqtype == DEVREQ_STAT )
				memcpy( buff, req->kbuff, sizeof( struct stat ) );
			else
			{
				if( ((size_t) (req->reqres * device->blksz)) > req->bytecount )
				{
					// don't let the copy run us over
					req->kbuff = (char*) realloc( req->kbuff, req->reqres * device->blksz );
					req->kbuff_phys = req->kbuff;
				}
				memcpy( buff, req->kbuff, req->reqres * device->blksz );
			}
		}
		else
			currprocess->filedescs[fd] = desc;

		// free the allocated pages
		DEVMAN_MEM_FREE( req->kbuff_phys );
	}
#endif

	// if the request was an open fill in the new descriptor
	if( reqtype == DEVREQ_OPEN )
		currprocess->filedescs[fd] = desc;

	// save the return value
	int32_t ret = req->reqres;

	// zero out the kernel buffer fields (just in case we get preempted here)
	req->kbuff = 0;
	req->kbuff_phys = 0;

	// unlink the request
#if !USE_STACK_NOT_MALLOC
	if(req->prev)
		req->prev->next = req->next;
	if( req->next )
		req->next->prev = req->prev;
	req->req = DEVREQ_INVALID;

	// free the request
	free( req );
#endif

	// re-enable interrupts, we're finished
	if( intstate & 0x200 )
		asm volatile( "sti" );

	// return whatever the call gave us
	devman_errno = E_DMAN_NOERR;
	return ret;
}

// the open system call
int32_t sys_open( const char* path, int32_t flags )
{
#if DEBUG
	dprintf( "sys_open\n" );
#endif

	// prepend the current directory if needed
	char* realpath;
	if( path[0] != '/' )
	{
		// keep 3 extra bytes spare
		realpath = (char*) malloc( strlen( path ) + strlen( currprocess->currdir ) + 3 );
		strcpy( realpath, currprocess->currdir );
		if( currprocess->currdir[strlen(currprocess->currdir) - 1] != '/' )
			strcat( realpath, "/" );
		strcat( realpath, path );
	}
	else
	{
		// copy the normal string
		realpath = (char*) malloc( strlen( path ) + 1 );
		strcpy( realpath, path );
	}

	// try to find the device
	struct dev* device;
	for( device = devlist; device != 0; device = device->next )
	{
		// check the prefix
		if( strncmp( device->prefix, realpath, strlen( device->prefix ) ) == 0 && device->id != -1 )
			break;
	}

	// check for a valid device
	if( device == 0 )
	{
		devman_errno = E_DMAN_INVALARG;
		free( realpath );
		return -1; // none found
	}

	// we're toying with process data so don't let us be interrupted
	uint32_t intstate;
	asm volatile( "pushf; pop %0; cli" : "=r" (intstate) );

	// grab a pointer to the current process (currprocess = ent->me, so it's fine)
	struct context* proc = currprocess;

	// try to find a spare descriptor
	size_t i;
	for( i = 0; i < 512; i++ )
		if( proc->filedescs[i].avail == 1 )
			break;

	// none found?
	if( i == 512 )
	{
		// re-enable interrupts
		if( intstate & 0x200 )
			asm volatile( "sti" );
		devman_errno = E_DMAN_NOROOM;
		free( realpath );
		return -1; // none found
	}

	// this is now no longer available
	proc->filedescs[i].avail = 0;
	proc->filedescs[i].devid = device->id;
	proc->filedescs[i].pid = getpid(); //proc->id;

	// use the common io handler to get the internal descriptor
	proc->filedescs[i].internaldesc = commonio_handling( DEVREQ_OPEN, i, realpath, strlen( realpath ), flags, 0, 0, 0 );

	// free the path string
	free( realpath );

	// re-enable interrupts
	if( intstate & 0x200 )
		asm volatile( "sti" );

	// did it fail?
	if( devman_errno != E_DMAN_NOERR )
		return -1;

	// return the file descriptor number
	if( proc->filedescs[i].internaldesc != -1 )
	{
		devman_errno = E_DMAN_NOERR;
		return i;
	}
	else
	{
		devman_errno = E_DMAN_FAILED;
		return -1;
	}

	// pigs are flying, look out your window
	return 0;
}

// the dup system call
int32_t sys_dup( int32_t fd )
{
#if DEBUG
	dprintf( "sys_dup\n" );
#endif

	dprintf("[DMAN ] dup called, but not fully implemented\n");

	// try to find a spare descriptor
	size_t i;
	for( i = 0; i < 512; i++ )
		if( currprocess->filedescs[i].avail == 1 )
			break;

	// none found?
	if( i == 512 )
	{
		devman_errno = E_DMAN_NOROOM;
		return -1; // none found
	}

	// take it as ours
	memcpy(&(currprocess->filedescs[i]), &(currprocess->filedescs[fd]), sizeof(struct fd));

	// use the common io handler
	devman_errno = E_DMAN_NOERR;
	int32_t ret = commonio_handling( DEVREQ_DUP, fd, 0, 0, i, 0, 0, 0 );
	if( devman_errno != E_DMAN_NOERR )
		return -1;
	return ret;
}

// the read system call
size_t sys_read( int32_t fd, const void* buff, size_t count )
{
#if DEBUG
	dprintf( "sys_read\n" );
#endif

	// use the common io handler
	devman_errno = E_DMAN_NOERR;
	int32_t ret = commonio_handling( DEVREQ_READ, fd, buff, count, 0, 0, 0, 0 );
	if( devman_errno != E_DMAN_NOERR )
		return -1;
	return ret;
}

// the write system call
size_t sys_write( int32_t fd, void* buff, size_t count )
{
#if DEBUG
	dprintf( "sys_write\n" );
#endif

	// use the common io handler
	devman_errno = E_DMAN_NOERR;
	int32_t ret = commonio_handling( DEVREQ_WRITE, fd, buff, count, 0, 0, 0, 0 );
	if( devman_errno != E_DMAN_NOERR )
		return -1;
	return ret;
}

// the seek system call
size_t sys_seek( int32_t fd, size_t off, int32_t set )
{
#if DEBUG
	dprintf( "sys_seek\n" );
#endif

	// use the common io handler
	devman_errno = E_DMAN_NOERR;
	int32_t ret = commonio_handling( DEVREQ_SEEK, fd, 0, 0, off, set, 0, 0 );
	if( devman_errno != E_DMAN_NOERR )
		return -1;
	return ret;
}

// the ioctl system call
int32_t sys_ioctl( int32_t fd, int32_t action, int32_t data )
{
#if DEBUG
	dprintf( "sys_ioctl\n" );
#endif

	// use the common io handler
	devman_errno = E_DMAN_NOERR;
	int32_t ret = commonio_handling( DEVREQ_IOCTL, fd, 0, 0, action, data, 0, 0 );
	if( devman_errno != E_DMAN_NOERR )
		return -1;
	return ret;
}

// the stat system call
int32_t sys_stat( int32_t fd, struct stat* buf  )
{
#if DEBUG
	dprintf( "sys_stat\n" );
#endif

	// use the common io handler
	devman_errno = E_DMAN_NOERR;
	int32_t ret = commonio_handling( DEVREQ_STAT, fd, (char*) buf, 0, 0, 0, 0, 0 );
	if( devman_errno != E_DMAN_NOERR )
		return -1;
	return ret;
}

// the read system call
int32_t sys_close( int32_t fd )
{
#if DEBUG
	dprintf( "sys_close\n" );
#endif

	// use the common io handler
	devman_errno = E_DMAN_NOERR;
	int32_t ret = commonio_handling( DEVREQ_CLOSE, fd, 0, 0, 0, 0, 0, 0 );

	if( devman_errno != E_DMAN_NOERR )
		return -1;

	// assume that fd = offset into the current process list's descriptors
	currprocess->filedescs[fd].avail = 1;

	// return whatever we got from the common handler
	return ret;
}
